package com.jwsoft.manager.core.task.address;

import cn.hutool.core.convert.Convert;
import com.alicp.jetcache.Cache;
import com.alicp.jetcache.anno.CacheType;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.bifang.engine.cache.jetcache.utils.JetCacheUtils;
import com.github.pagehelper.PageHelper;
import com.jwsoft.manager.common.enums.AreaEnum;
import com.jwsoft.manager.common.vo.eduAddressTree.AddressTreeVO;
import com.jwsoft.manager.core.dao.model.EduAddress;
import com.jwsoft.manager.core.dao.model.EduAddressTree;
import com.jwsoft.manager.core.dao.service.EduAddressService;
import com.jwsoft.manager.core.dao.service.EduAddressTreeService;
import com.jwsoft.manager.core.integration.eduAddressTree.impl.AddressTree;
import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.PostConstruct;
import java.time.Duration;
import java.util.List;

/**
 * 地址树变更后，同步与地址表的关联关系
 * 注意：该组件必须依赖于jetCache，如果jetCacheCreateCacheFactory对象改名，需要同步修改
 *
 * @author fangs
 */
@Component
@Slf4j
@DependsOn("jetCacheCreateCacheFactory")
public class AddressSyncXxlJob {
    /**
     * 如果某一节点的子节点数量大于MAX_HANDLE_NUM，则只修改标记，否则直接处理
     */
    private static final int MAX_HANDLE_NUM = 100;
    /**
     * 节点id对应街道编码缓存
     */
    private static Cache<Long, String> STREET_CODE_CACHE;
    /**
     * 子节点id对应父级节点id缓存
     */
    private static Cache<Long, Long> PARENT_ID_CACHE;
    @Autowired
    EduAddressTreeService eduAddressTreeService;
    @Autowired
    EduAddressService eduAddressService;

    @PostConstruct
    private void init() {
        STREET_CODE_CACHE = JetCacheUtils.create("streetCodeCache", CacheType.LOCAL, Duration.ofHours(1));
        PARENT_ID_CACHE = JetCacheUtils.create("parentIdCache", CacheType.LOCAL, Duration.ofHours(1));
    }

    @XxlJob("AddressSyncXxlJobHandler")
    public void addressSyncXxlJobHandler() {
        XxlJobHelper.log("XXL-JOB, AddressSyncXxlJobHandler");
        log.info("同步与地址表的关联关系开始");
        this.syncAddress();
        log.info("同步与地址表的关联关系结束");
    }

    private void syncAddress() {
        //1.获取地址树变更列表
        QueryWrapper<EduAddressTree> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(EduAddressTree::getChangeNode, 1);
        for (; ; ) {
            PageHelper.startPage(0, 500);
            List<EduAddressTree> addressTreeList = eduAddressTreeService.list(queryWrapper);
            if (CollectionUtils.isEmpty(addressTreeList)) {
                return;
            }
            for (EduAddressTree eduAddressTree : addressTreeList) {
                syncAddressOfOne(eduAddressTree);
            }
        }
    }

    private void syncAddressOfOne(EduAddressTree eduAddressTree) {
        if (eduAddressTree.getLeafNode() == 1 || StringUtils.hasText(eduAddressTree.getEduAddressId())) {
            //叶子节点
            AddressTreeVO addressTreeVO = Convert.convert(AddressTreeVO.class, eduAddressTree);
            syncAddressByLeafNode(addressTreeVO);
        }
        //是否末级节点
        if (eduAddressTree.getLastNode() == 0) {
            //非末级节点
            syncAddressForChild(eduAddressTree);
        } else {
            //末级节点,直接修改标记
            if (eduAddressTree.getChangeNode() == 1) {
                eduAddressTreeService.changeDown(eduAddressTree);
            }
        }
    }


    private void syncAddressForChild(EduAddressTree eduAddressTree) {
        QueryWrapper<EduAddressTree> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(EduAddressTree::getParentId, eduAddressTree.getAddressId());
        long count = eduAddressTreeService.count(queryWrapper);
        if (count > 0) {
            eduAddressTreeService.changeDown(eduAddressTree);
        }
        //如果子节点数量大于MAX_HANDLE_NUM，则只修改标记，否则直接处理
        if (count < MAX_HANDLE_NUM) {
            List<EduAddressTree> list = eduAddressTreeService.list(queryWrapper);
            if (CollectionUtils.isEmpty(list)) {
                return;
            }
            for (EduAddressTree child : list) {
                PARENT_ID_CACHE.put(child.getAddressId(), child.getParentId());
                syncAddressOfOne(child);
                PARENT_ID_CACHE.remove(child.getAddressId());
                STREET_CODE_CACHE.remove(child.getAddressId());
            }
        }
    }

    private void syncAddressByLeafNode(AddressTreeVO addressTreeVO) {
        try {
            if (CollectionUtils.isEmpty(addressTreeVO.getEduAddressIdList())) {
                throw new RuntimeException("地址树节点关联地址为空，节点id：" + addressTreeVO.getAddressId());
            }
            for (String eduAddressId : addressTreeVO.getEduAddressIdList()) {
                //2.获取地址表中的地址
                EduAddress address = eduAddressService.getById(eduAddressId);
                //3.根据地址树变更列表，更新地址表中的信息（街道编码、统筹区）
                String streetCode = getStreetCode(addressTreeVO);
                if (!StringUtils.hasText(streetCode)) {
                    log.error("获取地址树节点街道编码异常：{}", addressTreeVO.getAddressId());
                    continue;
                }
                if (streetCode.length() != 9) {
                    log.error("地址树节点街道编码长度异常，节点id：{},街道编码：{}", addressTreeVO.getAddressId(), streetCode);
                }
                String areaCode = streetCode.substring(0, 6);
                UpdateWrapper<EduAddress> updateWrapper = new UpdateWrapper<>();
                updateWrapper.lambda().eq(EduAddress::getAddressId, address.getAddressId());
                boolean changeFlag = false;
                if (!areaCode.equals(address.getAreaCode())) {
                    changeFlag = true;
                    updateWrapper.lambda().set(EduAddress::getAreaCode, areaCode);
                }
                if (!streetCode.equals(address.getStreetCode())) {
                    changeFlag = true;
                    updateWrapper.lambda().set(EduAddress::getStreetCode, streetCode);
                }
                if (changeFlag) {
                    eduAddressService.update(updateWrapper);
                }
            }
        } catch (Exception e) {
            log.error("同步地址异常，节点id：{}", addressTreeVO.getAddressId(), e);
        }
    }

    private String getStreetCode(AddressTreeVO addressTreeVO) {
        //如果当前节点为县/区、市、省，则街道编码为空
        if (AreaEnum.district.getType().equals(addressTreeVO.getAreaType())
                && AreaEnum.city.getType().equals(addressTreeVO.getAreaType())
                && AreaEnum.province.getType().equals(addressTreeVO.getAreaType())) {
            return null;
        }
        String streetCode = getStreetCode(addressTreeVO.getAddressId());
        if (!StringUtils.hasText(streetCode)) {
            AddressTree addressTree = AddressTree.getAddressTree(addressTreeVO);
            streetCode = addressTree.getStreetCode();
            setCache(addressTreeVO.getAddressId(), streetCode);
        }
        return streetCode;
    }

    private void setCache(Long treeId, String streetCode) {
        Long id = treeId;
        do {
            STREET_CODE_CACHE.put(id, streetCode);
            id = PARENT_ID_CACHE.get(id);
        } while (id != null);
    }

    private String getStreetCode(Long treeId) {
        String streetCode = STREET_CODE_CACHE.get(treeId);
        if (!StringUtils.hasText(streetCode)) {
            Long parentId = PARENT_ID_CACHE.get(treeId);
            if (parentId == null) {
                return null;
            }
            streetCode = getStreetCode(parentId);
        }
        return streetCode;
    }


}
